package com.jse.ee;

import java.io.BufferedReader;
import java.io.File;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.ObjectOutputStream;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.StandardSocketOptions;
import java.util.Date;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.ServiceLoader;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;

import com.jse.Jse;
import com.jse.Times;

import jakarta.servlet.ServletContainerInitializer;
import jakarta.servlet.ServletException;

public class JseServer {

	public static JseServletContext ctx=new JseServletContext();
	public static Set<Class<?>> classes=new HashSet<Class<?>>();
	public static Map<String,Object> RPC=new ConcurrentHashMap<>();
	int port=80;
	public JseServer() {
		
	}
	
	public void start() throws Exception {
		Map<String, String> infos = new LinkedHashMap<>();
        infos.put("Server version", "Jse/"+Jse.VERSION);
        infos.put("Server built", "2023-01-01 09:00:00");
        infos.put("OS Name\t", System.getProperty("os.name"));
        infos.put("OS Version", System.getProperty("os.version"));
        infos.put("Architecture",System.getProperty("os.arch"));
        infos.put("Java Home",System.getProperty("java.home"));
        infos.put("JVM Version",System.getProperty("java.runtime.version"));
        infos.put("JVM Vendor",System.getProperty("java.vm.specification.vendor"));
        infos.forEach((k,v)->{
        	System.out.println(k+":\t\t"+v);
        });
        RPC.put("test",new TestImpl());
        var serviceLoader=ServiceLoader.load(ServletContainerInitializer.class);//spi
        serviceLoader.forEach(init->{try {init.onStartup(classes,ctx);} catch (ServletException e) {e.printStackTrace();}});
        ctx.servlets.forEach((k,v)->{
        	try {
				v.init(new JseServletConfig());
			} catch (ServletException e) {
				e.printStackTrace();
			}
        });
        try (ServerSocket serverSocket = new ServerSocket(port,0, InetAddress.getByName("127.0.0.1"))) {
            serverSocket.setOption(StandardSocketOptions.SO_REUSEADDR, true);
            if(File.separatorChar=='/')serverSocket.setOption(StandardSocketOptions.SO_REUSEPORT, true);//win 无效
            while (true) {
                Socket socket = serverSocket.accept();
                Thread.startVirtualThread(() -> handle(socket));
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
	}
	void handle(Socket socket) {
        try (Socket s = socket) {
        	if(s.isClosed())return;
            var in = s.getInputStream();
            var out = s.getOutputStream();
            var reader=new BufferedReader(new InputStreamReader(in));
            String line=reader.readLine();
//            System.out.println("line0:"+line);
    		if(line==null) {//空消息
    			
    		}else if(line.startsWith("rpc:")) {// rpc:com.xxx.UserService
    			String key=line.substring(4);
    			System.out.println("rpc请求:"+key);
    			var objOut=new ObjectOutputStream(out);
                objOut.writeObject(RPC.get(key));
                objOut.flush();
    		}else
    		{
    			JseServerRequest request = new JseServerRequest(in,reader,line);
                request.localPort=s.getLocalPort();
                var remoteAddress=(InetSocketAddress)s.getRemoteSocketAddress();
                request.remoteAddr=remoteAddress.getAddress().getHostAddress();
                request.remotePort=remoteAddress.getPort();
                request.remoteHost=remoteAddress.getHostName();
                JseServerResponse response = new JseServerResponse(out,request);
//                Upgrade字段必须包含，值为websocket
//                Connection字段必须包含，值为Upgrade
//                Sec-WebSocket-Key字段必须包含 ，记录着握手过程中必不可少的键值。
//                Sec-WebSocket-Protocol字段必须包含 ，记录着使用的子协议
                if("GET".equals(request.getMethod())&&
                		"websocket".equals(request.getHeader("Upgrade"))) {
                	out.write("""
HTTP/1.1 101 Switching Protocols
Server: JSE/1.0.0
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Accept: HSmrc0sMlYUkAGmm5OPpG2HaGWk=
Sec-WebSocket-Protocol: chat""".formatted(Times.gmt(new Date())).getBytes());
                	out.flush();
                }else {
                	var servlet=ctx.servlets.get("jse");//本地直接取
                    servlet.service(request, response);
                    response.flushBuffer();//输出内容
                }
    		}
        } catch (Exception e) {
            // auto-close
        	e.printStackTrace();
        } finally {
           
        }
    }
	
	public static void main(String[] args) throws Exception {
		new JseServer().start();
	}
}
